package implementacionJ48;

import weka.core.*;

import java.io.IOException;
import java.util.*;

import no.geosoft.cc.graphics.GObject;

import implementacionJ48.Gain;

import implementacionJ48.Log;

public class NodoArbol {
	
	//los elementos del nodo del arbol
	public Instances S; // Conjunto que evalua el nodo actual
	public String nombreNodo; //nombre del nodo
	public double valorOptimoParticion;//Aqui guardamos el valor de particion de la columna(cuando tiene valores numericos)
	public int factor;
	public String arcoProveniente;
	public GObject objetoPadre;
	/* Estas dos variables indican en el nodo hoja la cantidad de casos cubiertos correctamente -> N
	 * y la cantidad de casos cubiertos incorrectamente -> E*/
	double N;
	double E;
	
	//public Attribute atributo;
	public List< NodoArbol > hijos; // lista de hijos del nodo
	static int  i = 0 ; //para probar el arbol.  
	public boolean isParent; //
	
	public static Log log;
	
	public NodoArbol(){
		this.S = null;
		this.hijos = new ArrayList <NodoArbol>();
		this.nombreNodo = null;
		this.N = 0.0;
		this.E = 0.0;
		this.isParent = false;
	} 	
	
	public NodoArbol(Log log){
		
		this.S = null;
		this.hijos = new ArrayList <NodoArbol>();
		this.nombreNodo = null;		
		this.N = 0.0;
		this.E = 0.0;
		
		this.log = log;
		
	} 
	
	//adhiere a la lista de hijos un nuevo nodo con las instancias T
	public void addHijo(NodoArbol nuevoNodo, Instances T)
	{
		//crea un nuevo nodo con un nuevo conjunto de instancias
		nuevoNodo.S = T;
		this.hijos.add(nuevoNodo);
		
	}
	
	public List<NodoArbol> getHijos()
	{
		return this.hijos;
	}
	
	
	/*
	 * LLamada principal que genera el arbol:  
	 * 
	 * 1. Primero se evalua si no se llego a una clase(nodo hoja). 
	 * 2. Se calcula cual de los atributos es el que se utilizara
     * 3. Se genera un nodo que se adhiere a los hijos del nodo actual
     * 4. Se llama recursivamente a la funcion
     * 5. Retorna cuando genero todos los hijos que pod
     * 
	 * */
	
	public void generarNodos (NodoArbol nodoActual, double porcentajeCerteza) throws Exception// el nodo inicial es el nodo raiz
	{

		/* 1. Se calcula cual de los atributos es el mejor para el el conjunto S del 
		 * nodo actual */
		Attribute bestAttr = null;
		bestAttr = Gain.getBestAttribute(nodoActual.S, porcentajeCerteza);
		double bestAttrValue = 0.0;
		
		/* Aqui calculamos  N --> (La cantidad de casos cubiertos por la hoja)
		 *  		      y E --> (La cantidad de casos cubiertos incorrectamente) */
		nodoActual.N = TestData.calculateNcasosCubiertos(nodoActual.S);
		nodoActual.E = TestData.calculateEcasosCubiertosErr(nodoActual.S);
		
		/* 2. Si no hay un mejor nodo para dividir, llegamos a un nodo hoja(clasificador) */ 
		if(bestAttr == null){
		    /* Obtenemos la clasificacion del nodo hoja */
            int valorClase = TestData.indiceClaseConMasFilas(nodoActual.S);
            //Instance instancia = S.instance(indiceClase);
            String clasificacion = TestData.nombreClase(S, valorClase);
            
            /* Al nombre del nodo le adherimos su N y E respectivos */
            //nodoActual.nombreNodo = clasificacion + " [" + Double.toString(nodoActual.N) + " : " + Double.toString(nodoActual.E) + "]";
            Double probCerteza = (1 - nodoActual.E/nodoActual.N)*100;
            String stringCerteza = String.format("%.1f", probCerteza);
            nodoActual.nombreNodo = clasificacion + " [" + stringCerteza + "]";
    		/* Escribimos en el log que clasificamos un nodo */
            log.addMensaje("Nodo hoja, clasificado en: " + clasificacion);
			return;
		}
		
		/* Seteamos el numero de valores del bestAttribute que recibimos 
		 * Para el caso de que el atributo tenga valores continuos, seteamos a dos(true o false) 
		 * y sino, obtenemos simplemente la cantidad de valores del atributo */ 
		int numValuesAttr = 0;
		if(bestAttr.isNumeric())
			numValuesAttr = 2;
		else
			numValuesAttr = bestAttr.numValues();
		
		/* Si los valores del atributo son numericos, guardamos el valor optimo de particion para luego mostrar en pantalla */
		double valorOptimoParticion = 0.0;
		if(bestAttr.isNumeric()){
			valorOptimoParticion = TestData.getValorOptimoParticion(bestAttr.index());
			nodoActual.valorOptimoParticion = valorOptimoParticion;
		}
		/* Seteamos el nombre del nodoActual(esto es solo para mostrar luego en pantalla) */
		nodoActual.nombreNodo = bestAttr.name() ;//+ ".(" + Double.toString(bestAttrValue) + ")"; 
		log.addMensaje("El atributo elegido para la particion fue: " + bestAttr.name());
		/* 3. Se genera un nodo por cada posible valor del atributo bestAttr y adherimos
		 *    a los hijos del nodo actual(o padre) */
		
		for(int i = 0; i < numValuesAttr; i++){
			NodoArbol nodoHijo = new NodoArbol();
			nodoHijo.S = TestData.selectInstancesWhere(S, bestAttr, bestAttrValue);
			nodoHijo.factor = i;
			
			/* Nombre del arco del que proviene, si es numerico hay que setear el valor de particion(ej. <= 70) */ 
			if(bestAttr.isNumeric()){
				if(bestAttrValue == 0.0)
					nodoHijo.arcoProveniente = "<=" + TestData.valorOptimoParticion[bestAttr.index()];
				else
					nodoHijo.arcoProveniente = ">" + TestData.valorOptimoParticion[bestAttr.index()];
			}else{
				nodoHijo.arcoProveniente = bestAttr.value((int)bestAttrValue);
			}
			/* Si los valores del atributo son numericos, guardamos el valor optimo de particion para luego mostrar en pantalla */
			valorOptimoParticion = 0.0;
			if(bestAttr.isNumeric()){
				valorOptimoParticion = TestData.getValorOptimoParticion(bestAttr.index());
				nodoHijo.valorOptimoParticion = valorOptimoParticion;
			}
			/* Seteamos el nombre del nodoActual(esto es solo para mostrar luego en pantalla) */
			//nodoHijo.nombreNodo = bestAttr.name() + ".(" + Double.toString(bestAttrValue) + ")";
			
			/* 4. Adherimos el nuevo hijo a su padre */
			/* Adherimos el nuevo hijo a su padre */
			nodoActual.hijos.add(nodoHijo);
			
			/* Seteamos el posible siguiente valor */
			bestAttrValue +=1.0;
			
			/* hacemos la llamada recursiva para seguir generando los demas hijos */
			nodoHijo.generarNodos(nodoHijo, porcentajeCerteza);

		}
	}
}
